package net.kazed.nextaction.database.serialize;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class XmlParser {

    private XmlEncoder xmlEncoder;
    private ElementHandler rootElementHandler;
    private ElementHandler dummyElementHandler;

    /**
     * Constructor.
     */
    public XmlParser(XmlEncoder xmlEncoder, ElementHandler rootElementHandler) {
        super();
        this.xmlEncoder = xmlEncoder;
        this.rootElementHandler = rootElementHandler;
        dummyElementHandler = new DummyElementHandler();
    }

    public void parse(File file) {
        SAXParser parser = null;
        try {
            parser = SAXParserFactory.newInstance().newSAXParser();
        } catch (ParserConfigurationException e) {
            throw new ParseException("Parser configuration", e);
        } catch (SAXException e) {
            throw new ParseException("Parser startup", e);
        }

        Handler handler = new Handler(rootElementHandler);
        try {
            parser.parse(file, handler);
        } catch (SAXException e) {
            throw new ParseException("Failed to parse", e);
        } catch (IOException e) {
            throw new ParseException("Failed to read", e);
        }
    }

    private class Handler extends DefaultHandler {

        private ParserState parserState;
        private ElementHandler currentElementHandler;

        /**
         * Constructor.
         */
        public Handler(ElementHandler rootElementHandler) {
            parserState = new ParserState();
            this.currentElementHandler = rootElementHandler;
        }

        @Override
        public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
            String elementName = name;
            if (elementName == null || elementName.length() == 0) {
                elementName = localName;
            }
            parserState.pushElementHandler(currentElementHandler);
            currentElementHandler = currentElementHandler.getElementHandler(elementName);
            if (currentElementHandler == null) {
                currentElementHandler = dummyElementHandler;
            }
            currentElementHandler.startElement(elementName, attributes, parserState);
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            String text = new String(ch, start, length);
            if (currentElementHandler.isValueEncoded()) {
                text = xmlEncoder.decode(text);
            }
            currentElementHandler.characters(text, parserState);
        }

        @Override
        public void endElement(String uri, String localName, String name) throws SAXException {
            String elementName = name;
            if (elementName == null || elementName.length() == 0) {
                elementName = localName;
            }
            currentElementHandler.endElement(elementName, parserState);
            currentElementHandler = parserState.popElementHandler();
        }

    }

    private class DummyElementHandler implements ElementHandler {

        @Override
        public void characters(String text, ParserState state) {
        }

        @Override
        public void endElement(String name, ParserState state) {
        }

        @Override
        public void startElement(String name, Attributes attributes, ParserState state) {
        }

        @Override
        public ElementHandler getElementHandler(String elementName) {
            return null;
        }

        @Override
        public boolean isValueEncoded() {
            return false;
        }
    }

}
